package replay

import (
	"context"
	"time"

	"code.byted.org/bytertc/whiteboard_server/biz/pkg/object_storage"

	"code.byted.org/bytertc/whiteboard_server/biz/api"
	"code.byted.org/bytertc/whiteboard_server/biz/config"
	"code.byted.org/bytertc/whiteboard_server/biz/dal"
	"code.byted.org/bytertc/whiteboard_server/biz/dal/spec"
	"code.byted.org/bytertc/whiteboard_server/biz/pkg/biz_error"
	"code.byted.org/bytertc/whiteboard_server/biz/pkg/gateway"
	"code.byted.org/gopkg/logs/v2/log"
	"code.byted.org/videoarch/universal_utils_go/event_collector/tracer"
	"code.byted.org/videoarch/universal_utils_go/event_collector/util"
	"github.com/pkg/errors"
	"github.com/spf13/cast"
)

type Manager interface {
	CloseBoard(ctx context.Context, unique spec.BoardUnique) error
	CloseRoom(ctx context.Context, unique spec.RoomUnique) error
	GetListReplay(ctx context.Context, unique spec.BoardUnique) ([]api.File, error)
	StartVideoRecord(ctx context.Context, req *api.StartVideoRecordRequest) error
	StopVideoRecord(ctx context.Context, req *api.StopVideoRecordRequest) error
	GetFirstBoard(ctx context.Context, unique spec.RoomUnique) (int32, error)
}

func NewManager(repo dal.Repo, config config.DynamicConfig, rtcAPI gateway.RtcAPI) Manager {
	return &manager{
		rtcAPI: rtcAPI,
		repo:   repo,
		config: config,
	}
}

type manager struct {
	rtcAPI gateway.RtcAPI
	repo   dal.Repo
	config config.DynamicConfig
	//bucket string
}

func (m *manager) CloseBoard(ctx context.Context, unique spec.BoardUnique) error {
	ok, err := m.repo.CloseBoard(ctx, unique, unique.GetBoardId())
	if err != nil {
		log.V1.CtxError(ctx, "CloseBoard err:%+v", err)
		return err
	}
	if !ok {
		return nil
	}
	traceKey := &util.KeyWord{
		RoomId: unique.GetRoomId(),
		AppId:  unique.GetAppId(),
		Tag:    "CloseWhiteboard",
	}
	tracer.MileStone(tracer.STREAM_COLLECTOR, "close_white_board", "begin:"+cast.ToString(time.Now().Unix()), traceKey, map[string]string{"boardId": cast.ToString(unique.GetBoardId())})

	maxId, err := m.repo.GetMaxPacketId(ctx, unique)
	if err != nil {
		log.V1.CtxError(ctx, "CloseBoard err:%+v", err)
		return err
	}
	r := New(ctx, m.repo, m.config)
	if !r.InitConsoleConfig(ctx, unique) {
		return nil
	}
	//此处是异步处理，有失败风险，后续需要优化
	go func() {
		err = r.GenerateFile(ctx, unique, maxId)
		if err == nil {
			traceKey := &util.KeyWord{
				RoomId: unique.GetRoomId(),
				AppId:  unique.GetAppId(),
				Tag:    "CloseBoard",
			}
			tracer.MileStone(tracer.STREAM_COLLECTOR, "generate_file_finally", "end:"+cast.ToString(time.Now().Unix()), traceKey, map[string]string{"boardId": cast.ToString(unique.GetBoardId())})
		}
	}()
	return nil
}
func (m *manager) GetListReplay(ctx context.Context, unique spec.BoardUnique) ([]api.File, error) {
	replays, err := m.repo.ListReplay(ctx, unique)
	if err != nil {
		return nil, err
	}

	files := make([]api.File, len(replays))
	for index, replay := range replays {
		vendor := object_storage.VendorVolcTos
		osClient, err := GetObjectStorage(ctx, vendor, replay.AccountId, replay.Bucket, replay.Region)
		if err != nil {
			log.V1.CtxError(ctx, "get object storage failed,os_type:%s,bucket:%s,error:%s", vendor, replay.Bucket, err)
			return nil, err
		}
		files[index].Bucket = replay.Bucket
		files[index].Name = replay.FileName
		files[index].URL, _ = osClient.PreSignURL(ctx, replay.FileName, 12*time.Hour)
		files[index].StartTime = replay.StartTime
		files[index].EndTime = replay.EndTime
	}
	return files, nil
}

func (m *manager) GetFirstBoard(ctx context.Context, unique spec.RoomUnique) (int32, error) {
	return m.repo.GetFirstBoardId(ctx, unique)
}

func (m *manager) StartVideoRecord(ctx context.Context, req *api.StartVideoRecordRequest) error {
	sr := &api.SwitchStatusRequest{
		AppID:    req.AppId,
		Category: dal.Category_Switch_Record,
	}
	switchInfos, err := m.repo.GetSwitchInfo(ctx, sr)
	if err != nil {
		return err
	}
	if len(switchInfos) == 0 {
		return biz_error.InvalidParam.Wrap(errors.Errorf("not find any console config"))
	}
	if switchInfos[0].Status == dal.Switch_OFF {
		return biz_error.InvalidParam.Wrap(errors.Errorf("console swtich not open"))
	}
	cr := &api.GetConsoleInfoRequest{
		AppID:    req.AppId,
		Category: dal.Category_Video_Record,
	}
	configInfos, err := m.repo.GetConfig(ctx, cr, false)
	if err != nil {
		return err
	}
	if len(configInfos) == 0 {
		return biz_error.InvalidParam.Wrap(errors.Errorf("not find any video console config"))
	}
	spaceType := configInfos[0].SpaceType
	space := configInfos[0].Space
	if space == "" {
		return biz_error.InvalidParam.Wrap(errors.Errorf("config sapce is empty"))
	}
	accountId := configInfos[0].AccountId
	if accountId == "" {
		return biz_error.InvalidParam.Wrap(errors.Errorf("accountId sapce is empty"))
	}

	srp := &gateway.StartRecordParams{
		AppId:         req.AppId,
		RoomID:        req.RoomID,
		TaskId:        req.TaskId,
		RecordMode:    1,
		StorageConfig: gateway.StorageConfig{},
	}

	if spaceType == dal.Space_Type_TOS {
		srp.StorageConfig.Type = 0
		srp.StorageConfig.TosConfig.Bucket = space
		srp.StorageConfig.TosConfig.AccountId = accountId

	} else if spaceType == dal.Space_type_VOD {
		srp.StorageConfig.Type = 1
		srp.StorageConfig.VodConfig.Space = space
		srp.StorageConfig.VodConfig.AccountId = accountId
	} else {
		err := errors.Errorf("config sapceType is invalid")
		log.V1.CtxError(ctx, "err:%+v,appId:%s", err, req.AppId)
		return biz_error.InvalidParam.Wrap(err)
	}
	stream := gateway.Stream{
		Index:  0,
		UserId: req.UserId,
	}
	srp.TargetStreams = gateway.Streams{StreamList: []gateway.Stream{stream}}
	err = m.rtcAPI.StartRecord(ctx, srp)
	if err != nil {
		return err
	}
	swp := &gateway.StartWebCastParams{
		AppId:      req.AppId,
		BusinessId: req.BusinessId,
		RoomID:     req.RoomID,
		TaskId:     req.TaskId,
		UserId:     req.UserId,
		SourceURL:  req.SourceURL,
		Monitor: &gateway.MonitorConfig{
			CrashCheckInterval: 5,
		},
	}
	return m.rtcAPI.StartWebCast(ctx, swp)
}

func (m *manager) StopVideoRecord(ctx context.Context, req *api.StopVideoRecordRequest) error {
	swp := &gateway.StopWebCastParams{
		AppId:  req.AppId,
		RoomID: req.RoomID,
		TaskId: req.TaskId,
	}
	err := m.rtcAPI.StopWebCast(ctx, swp)
	if err != nil {
		return err
	}
	srp := &gateway.StopRecordParams{
		AppId:  req.AppId,
		RoomID: req.RoomID,
		TaskId: req.TaskId,
	}
	return m.rtcAPI.StopRecord(ctx, srp)

}

func (m *manager) CloseRoom(ctx context.Context, unique spec.RoomUnique) error {
	err := m.repo.CloseRoom(ctx, unique)
	if err != nil {
		log.V1.CtxError(ctx, "CloseRoom err:%+v", err)
		return err
	}
	err = m.repo.CloseBoards(ctx, unique)
	if err != nil {
		log.V1.CtxError(ctx, "CloseBoards err:%+v", err)
		return err
	}
	traceKey := &util.KeyWord{
		RoomId: unique.GetRoomId(),
		AppId:  unique.GetAppId(),
		Tag:    "CloseWhiteboard",
	}
	tracer.MileStone(tracer.STREAM_COLLECTOR, "close_white_board", "begin:"+cast.ToString(time.Now().Unix()), traceKey, map[string]string{"boardId": cast.ToString(0)})

	maxId, err := m.repo.GetMaxPacketId(ctx, unique)
	if err != nil {
		log.V1.CtxError(ctx, "CloseRoom err:%+v", err)
		return err
	}
	r := New(ctx, m.repo, m.config)
	if !r.InitConsoleConfig(ctx, unique) {
		return nil
	}
	//此处是异步处理，有失败风险，后续需要优化
	go func() {
		err = r.GenerateFile(ctx, unique, maxId)
		if err == nil {
			traceKey := &util.KeyWord{
				RoomId: unique.GetRoomId(),
				AppId:  unique.GetAppId(),
				Tag:    "CloseBoard",
			}
			tracer.MileStone(tracer.STREAM_COLLECTOR, "generate_file_finally", "end:"+cast.ToString(time.Now().Unix()), traceKey, map[string]string{"boardId": cast.ToString(0)})
		}
	}()
	return nil
}
